package com.winning.jsyd.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.winning.jsyd.entity.describe.*;
import com.winning.jsyd.entity.enums.ServiceEnum;
import com.winning.jsyd.service.UDService;
import com.winning.jsyd.utils.JAXBUtils;
import com.winning.jsyd.utils.NetDataUtil;
import com.winning.jsyd.ws.ISupHibms;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @author z_dd@winning.com.cn
 * @date 2019/5/5 13:55
 */
@Slf4j
@RestController(value = "udc")
public class UDController {

    @Autowired
    UDService udService;
    @Value("${jsyd.auth.userCode:320903000000}")
    private String userCode; //  医院编码

    @Value("${jsyd.auth.unitsCode:320903000000}")
    private String unitsCode; //  操作员号

    @Value("${jsyd.auth.licenseId:1}")
    private String licenseId; //  授权码

    @Value("${jsyd.dataupload.singleBatchSize:100}")
    private int singleSize;//   每页大小

    @Value("${jsyd.dataupload.singleBatchPages}")
    private int singleBatchPages;//单次同时将一定数量的查询、上传页数任务加入队列

    @Autowired
    private ISupHibms iSupHibmsPort;

    private Map<String, String> records = new HashMap<String, String>();

    @GetMapping("task")
    public void call(String pam) {
        ServiceEnum serviceEnum = null;
        if (pam.equals("1")) {
            serviceEnum = ServiceEnum.HaPersonInfo;
        } else if (pam.equals("2")) {
            serviceEnum = ServiceEnum.DmHighbloodReport;
        } else if (pam.equals("3")) {
            serviceEnum = ServiceEnum.DmDiabetesReport;
        } else if (pam.equals("4")) {
            serviceEnum = ServiceEnum.DmHighbloodVisit;
        } else if (pam.equals("5")) {
            serviceEnum = ServiceEnum.DmDiabetesVisit;
        } else if (pam.equals("6")) {
            serviceEnum = ServiceEnum.MssRcptMaster;
        } else if (pam.equals("7")) {
            serviceEnum = ServiceEnum.MssOutDiagnosis;
        } else if (pam.equals("8")) {
            serviceEnum = ServiceEnum.MssPrescMaster;
        } else if (pam.equals("9")) {
            serviceEnum = ServiceEnum.MssExamMaster;
        } else if (pam.equals("A")) {
            serviceEnum = ServiceEnum.MssTestMaster;
        } else if (pam.equals("B")) {
            serviceEnum = ServiceEnum.MssPatsBillDetail;
        } else if (pam.equals("C")) {
            serviceEnum = ServiceEnum.MssPatsSettleMaster;
        } else if (pam.equals("D")) {
            serviceEnum = ServiceEnum.MssInOrder;
        } else if (pam.equals("E")) {
            serviceEnum = ServiceEnum.HMssPrescMaster;
        } else if (pam.equals("F")) {
            serviceEnum = ServiceEnum.MssResidentAdmin;
        } else if (pam.equals("G")) {
            serviceEnum = ServiceEnum.MssOperationRecord;
        } else if (pam.equals("H")) {
            serviceEnum = ServiceEnum.MssDischarge;
        } else if (pam.equals("I")) {
            serviceEnum = ServiceEnum.MssOperationApply;
        } else if (pam.equals("test")) {
            test1();
        }
        task(serviceEnum);
    }

    public void task(ServiceEnum serviceEnum) {

        Record record = new Record(serviceEnum.getTableName());
//        记录任务开始时间
        records.put(serviceEnum.getTableName(), NetDataUtil.date(new Date()));

        Page page = new Page();
        page.setCurrent(1);
        //                    page.setSize(singleSize > 0 ? singleSize : 100);
        page.setSize(serviceEnum.getSize() > 0 ? serviceEnum.getSize() : singleSize);

        try {
//            String businessNo = checkIn();
//            记录任务唯一编号
            record.setTaskId(checkIn());

            Class<? extends UDService> aClass = udService.getClass();
            Method method = aClass.getMethod(serviceEnum.getInterfaceName(), Page.class, ServiceEnum.class, String.class);
            ResultSet iPage = (ResultSet) method.invoke(udService, page, serviceEnum, records.get(serviceEnum.getTableName()));

            record = check(iPage.getRecords(), serviceEnum, record);
            if (!record.isFlag()) {
                return;
            }

            record = remainingTasks(iPage.getPages(), serviceEnum, record);

//        记录任务结束时间
            record.setEndingTime(NetDataUtil.date(new Date()));

            log.info("最终--上传记录:" + record);

//            记录日志
            udService.statisticalResults(record);


            checkOut(record);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送请求
     *
     * @param records
     * @param serviceEnum
     * @param businessNo
     * @return
     */
    private String sendRecords(List<DataList> records, ServiceEnum serviceEnum, String businessNo) throws Exception {
        log.debug("校验:{},{}",Objects.isNull(records) ,records.size());
        if (Objects.isNull(records) || records.size() < 1) {
            return null;
        }
        String xml = splicingParam(records, businessNo);
        Class<? extends ISupHibms> aClass = iSupHibmsPort.getClass();
        Method method = aClass.getMethod(serviceEnum.getInterfaceName(), String.class);
        log.error("入参xml==========\n" + xml);
        String responXml = (String) method.invoke(iSupHibmsPort, xml);
        log.error("返回XML____________________________\n"+responXml);
        return responXml;
    }


    private Record remainingTasks(long pages, ServiceEnum serviceEnum, Record record) {

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List<Callable<Record>> callables = new ArrayList<>();
        int remainningPages = (int) pages - 1;
        int poolSize = remainningPages / singleBatchPages;
        poolSize = poolSize > 0 ? (poolSize + (remainningPages % singleBatchPages == 0 ? 0 : 1)) : 1;
        int pas;

        for (int pool = 1; pool <= poolSize; pool++) {
            if (poolSize == 1 || pool == poolSize) {
                pas = remainningPages;
            } else {
                pas = pool * singleBatchPages;
            }
            for (int currentPage = (pool - 1) * singleBatchPages + 1; currentPage <= pas; currentPage++) {
                final int i = currentPage + 1;
                Callable<Record> task = () -> {
                    Page page = new Page();
                    page.setCurrent(i);
//                    page.setSize(singleSize > 0 ? singleSize : 100);
                    page.setSize(serviceEnum.getSize() > 0 ? serviceEnum.getSize() : singleSize);
                    Class<? extends UDService> aClass = udService.getClass();
                    Method method = aClass.getMethod(serviceEnum.getInterfaceName(), Page.class, ServiceEnum.class, String.class);
                    ResultSet iPage = (ResultSet) method.invoke(udService, page, serviceEnum, records.get(serviceEnum.getTableName()));

                    List<DataList> records = iPage.getRecords();
                    if (records == null || records.size() < 1) {
                        return record;
                    }
                    String resultXML = sendRecords(records, serviceEnum, record.getTaskId());
                    return parseResponse(resultXML, record);
                };
                callables.add(task);
            }
            try {

                List<Future<Record>> futures = executorService.invokeAll(callables);

                int i = 1;
                for (Future<Record> future : futures) {
                    Record record1 = future.get();
                    log.info("第{}次循环:\n{}", i, record1);
                    i++;
                }
            } catch (Exception e) {
                log.error(serviceEnum.getTableName() + ":任务执行期间出现问题", e);
            }

            callables.clear();
        }

        log.info("上传任务执行成功：" + serviceEnum.getTableName());
        udService.insertDate(serviceEnum.getTableName(), records.get(serviceEnum.getTableName()));
        return record;

    }

    /**
     * 解析处理响应结果
     *
     * @param response
     * @return
     */
    private synchronized Record parseResponse(String response, Record record) throws JAXBException {
        log.info("{}线程开始", Thread.currentThread().getName());
        log.debug("返回结果++++++++++++\n" + response);

        if (StringUtils.isEmpty(response)) {
            return record;
        }
        Unmarshaller unmarshaller = JAXBUtils.createUnMarshaller(WebServiceEntity.class);

        StringReader stringReader = new StringReader(response);

        WebServiceEntity webServiceEntity = (WebServiceEntity) unmarshaller.unmarshal(stringReader);

        log.debug("webServiceEntity" + webServiceEntity);
        log.debug(webServiceEntity.getResponse() + "======getResponse()======");
        log.debug(webServiceEntity.getResponse().getString() + "======getResponse().getString()======");

        List<String> errors = webServiceEntity.getResponse().getString();


        HeadEntity headEntity = webServiceEntity.getHead();

        log.debug("成功数:" + headEntity.getSuccessNum());
        log.debug("失败数:" + headEntity.getFailNum());
        record.setSuccessCount(record.getSuccessCount() + headEntity.getSuccessNum());
        record.setFailCount(record.getFailCount() + headEntity.getFailNum());


        if (errors != null && errors.size() >0) {
            log.debug("错误记录:\n{}" ,errors);
            if(errors.size() >1)
            errors.remove(errors.size() - 1);
            //记录错误日志
            udService.insertErrorLog(record.getTaskId(), record.getTableName(), errors);
        }
        log.info("{}线程结束,\n{}", Thread.currentThread().getName(), record);
        return record;
    }

    private Record check(List<DataList> iPage, ServiceEnum serviceEnum, Record record) throws Exception {
//        log.debug("开始\n{}",iPage.get(0));
        if (iPage == null || iPage.size() < 1) {
            record.setFlag(false);
            return record;
        }
        String resultXML = sendRecords(iPage, serviceEnum, record.getTaskId());
//        if (!parseResponse(sendRecords(iPage.getRecords(), serviceEnum)))
//            return false;
        log.debug("结束");
        return parseResponse(resultXML, record);
    }

    //  注销
    public void checkOut(Record record) {
        iSupHibmsPort.checkOut("<WebService><request><businessNo>" + record.getTaskId() + "</businessNo></request><head /></WebService>");
        log.info("签出成功");
    }

    //    签到
    public String checkIn() {
        String responseXml = iSupHibmsPort.checkIn("<WebService><request> <userCode>" + userCode + "</userCode><unitsCode>" + unitsCode + "</unitsCode><licenseId>" + licenseId + "</licenseId> </request></WebService>");
        StringReader sr = new StringReader(responseXml.replaceAll("webService", "WebService"));
        Unmarshaller unmarshaller = null;
        try {
            unmarshaller = JAXBUtils.createUnMarshaller(WebServiceEntity.class);
            WebServiceEntity webServiceEntity = (WebServiceEntity) unmarshaller.unmarshal(sr);
            log.info("签到成功:=======");
            return webServiceEntity.getResponse().getBusinessNo();
        } catch (JAXBException e) {
            log.error("签到错误:{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }

    }

    /**
     * 拼接请求参数
     *
     * @param records    request>dataList's
     * @param businessNo
     * @return
     * @throws JAXBException
     */
    private String splicingParam(List<DataList> records, String businessNo) throws JAXBException {
        HeadEntity head = new HeadEntity();
        head.setSendTime(NetDataUtil.date(new Date()));
        head.setUserIdCard("0");
        head.setUnitsName("bb");
        head.setDataSize(records.size());

        AuthorUserInfoEntity authorUserInfo = new AuthorUserInfoEntity();
        authorUserInfo.setBusinessNo(businessNo);
        authorUserInfo.setUnitsCode(unitsCode);
        authorUserInfo.setUserCode(userCode);

        RequestEntity request = new RequestEntity();
        request.setAuthorUserInfo(authorUserInfo);

        request.setDataList(records);

        WebServiceEntity webService = new WebServiceEntity();
        webService.setHead(head);
        webService.setRequest(request);

        StringWriter xmlWriter = new StringWriter();
        JAXBContext jaxbContext = JAXBUtils.createJAXBContext(WebServiceEntity.class);
        Marshaller marshaller = JAXBUtils.createMarshaller(jaxbContext);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
        marshaller.marshal(webService, xmlWriter);
        String xml = xmlWriter.toString();

        xml = JAXBUtils.replaceNameSpace(xml, null);
        return xml;
    }

    public  void test1(){

        udService.statisticalResults(new Record("11"));
    }

}
